package cn.itsource.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.IdUtil;
import cn.itsource.api.openfeign.ILoginApiToService;
import cn.itsource.constants.CacheConstants;
import cn.itsource.constants.Constants;
import cn.itsource.constants.MQConstants;
import cn.itsource.constants.RConst;
import cn.itsource.domain.Driver;
import cn.itsource.domain.DriverSetting;
import cn.itsource.exception.GlobalException;
import cn.itsource.mapper.DriverMapper;
import cn.itsource.domain.DriverAggrement;
import cn.itsource.pojo.dto.LoginRo;
import cn.itsource.pojo.dto.PushOrder;
import cn.itsource.pojo.dto.RegisterDto;
import cn.itsource.pojo.ro.DriverOnLineRo;
import cn.itsource.pojo.ro.GeoRo;
import cn.itsource.pojo.ro.WxRegisterRo;
import cn.itsource.result.JSONResult;
import cn.itsource.service.*;
import cn.itsource.utils.NameUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.consumer.PullResult;
import org.apache.rocketmq.client.consumer.PullStatus;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 司机对象 服务实现类
 * </p>
 *
 * @author zls
 * @since 2024-07-02
 */
@Service
@Slf4j
@Transactional
public class DriverServiceImpl extends ServiceImpl<DriverMapper, Driver> implements IDriverService {
    @Autowired
    private IDriverAggrementService driverAggrementService;

    @Autowired
    private WxService wxService;

    @Autowired
    private ILoginApiToService loginApiToService;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Autowired
    private IDriverSettingService driverSettingService;

    @Autowired
    private DefaultMQPullConsumer pullConsumer;

    @Override
    public JSONResult registerDriver(WxRegisterRo wxRegisterRo) {
        RegisterDto registerDto = wxService.getOpenId(wxRegisterRo.getCode());
        //获取电话号码
        //String phone = wxService.getPhone(wxRegisterRo.getPhoneCode());
        String phone ="19742322332";
        String name = NameUtil.getName();
        //查询当前司机是否已经注册
        Driver driver = super.getOne(new LambdaQueryWrapper<Driver>().eq(Driver::getOpenId,registerDto.getOpenid()));
        if (driver==null){
            //初始化
            Long driverId = initDriver(registerDto, phone);
            loginApiToService.saveLogin(buildLoginRo(registerDto,name,driverId));
            return JSONResult.message(Constants.WxChat.SUCCESS_MESSAGE);
        }
        return JSONResult.message(Constants.WxChat.ALREADY_BOUND);
    }

    /**
     * 司机上线
     * @param driverOnLineRo
     * @return
     */
    @Override
    public DriverSetting online(DriverOnLineRo driverOnLineRo) {
        String openId = StpUtil.getExtra("openId").toString();
        Driver driver = super.getOne(new LambdaQueryWrapper<Driver>().eq(Driver::getOpenId,openId));
        String key = String.format(CacheConstants.KEY_DRIVER_ONLINE_SETTING, driver.getId());
        DriverSetting setting = driverSettingService.getById(driver.getId());
        redisTemplate.opsForValue().set(key,setting,RConst.Num.BASE_LONG_5, TimeUnit.MINUTES);
        return setting;
    }

    @Override
    public void offLine() {
        String openId = StpUtil.getExtra("openId").toString();
        Driver one = super.getOne(new LambdaQueryWrapper<Driver>().eq(Driver::getOpenId, openId));
        String key = String.format(CacheConstants.KEY_DRIVER_ONLINE_SETTING, one.getId());
        redisTemplate.delete(key);
    }

    @Override
    public void geo(GeoRo geoRo) {
        //获取当前登录的司机id
        String openId = StpUtil.getExtra("openId").toString();
        Driver one = super.getOne(new LambdaQueryWrapper<Driver>().eq(Driver::getOpenId, openId));
        //Geo的坐标
        Point point = new Point(Double.valueOf(geoRo.getLongitude()),Double.valueOf(geoRo.getLatitude()));
        Long add = redisTemplate.opsForGeo().add(CacheConstants.KEY_DRIVER_GEO, point, one.getId());
        //判断是否添加成功
        if (add==RConst.Num.LONG_1){
            //刷新司机的状态
            String key = String.format(CacheConstants.KEY_DRIVER_ONLINE_SETTING, one.getId());
            redisTemplate.expire(key,RConst.Num.INT_5,TimeUnit.MINUTES);
        }

    }

    @Override
    public List<PushOrder> pullDriverOrder() {
        //司机的ID
        Long loginId = Long.valueOf(StpUtil.getLoginId().toString());
        try {
            //订阅消息队列，指定topic
            //Constants.MQ.TOPIC_ORDER+":"+String.format(Constants.MQ.TAGS_ORDER_DRIVER,driverId);
            Set<MessageQueue> messageQueues = pullConsumer.fetchSubscribeMessageQueues(MQConstants.PUSH_ORDER_TO_DRIVER_TOPIC);
            //封装结果
            List<PushOrder> result = new ArrayList<>();
            for (MessageQueue messageQueue : messageQueues) {
                //通过偏移量来获取消息 ：当前队列的消费位置
                long offset = pullConsumer.fetchConsumeOffset(messageQueue, true);
                //拉取消息，需要指定tags拼接司机ID， 来区分司机:
                String tags = String.format(MQConstants.PUSH_ORDER_TO_DRIVER_TAG, loginId);
                PullResult pullResult = pullConsumer.pull(messageQueue, tags, offset, 32);
                //找到消息
                if (pullResult != null && pullResult.getPullStatus().equals(PullStatus.FOUND)) {
                    //拿到消息列表
                    List<MessageExt> messageExtList = pullResult.getMsgFoundList();
                    if(messageExtList == null || messageExtList.size() == 0)continue;
                    for (MessageExt messageExt : messageExtList) {
                        //拿到消息内容，转换为订单对象
                        String message = new String(messageExt.getBody(), StandardCharsets.UTF_8);
                        PushOrder pushOrder = JSON.parseObject(message, PushOrder.class);
                        //添加到结果列表
                        result.add(pushOrder);
                        log.info("获取订单消息 {}",pushOrder);
                    }
                }
                if(pullResult != null){
                    //重要：修改消息的消费位置，如果位置不后移，消息会一直被消费
                    pullConsumer.updateConsumeOffset(messageQueue,pullResult.getNextBeginOffset());
                }
            }
            return result;
        } catch (Exception e) {
            throw new GlobalException("订单拉取失败");
        }
    }

    private static LoginRo buildLoginRo(RegisterDto registerDto,String name,Long id) {
        LoginRo loginRo = new LoginRo();
        loginRo.setPassword("123456");//todo
        loginRo.setType(RConst.Num.INT_1);
        loginRo.setOpenId(registerDto.getOpenid());
        loginRo.setUsername(name);//todo
        loginRo.setAdmin(false);
        loginRo.setId(id);
        return loginRo;
    }

    private Long initDriver(RegisterDto registerDto,String phone) {
        //初始化司机表
        Long snowflake = IdUtil.createSnowflake(RConst.Num.INT_1, RConst.Num.INT_1).nextId();
        Driver driver = new Driver()
                .setId(snowflake)
                .setPhone(phone)
                .setOpenId(registerDto.getOpenid())
                .setCreateTime(new Date());
        super.save(driver);
        //初始化 合同表
        Long driverId = IdUtil.createSnowflake(RConst.Num.INT_1, RConst.Num.INT_1).nextId();
        DriverAggrement driverAggrement = new DriverAggrement()
                .setId(driverId)
                .setDriverId(snowflake)
                .setCreateTime(new Date())
                .setStatus(RConst.Num.INT_0);
        driverAggrementService.save(driverAggrement);
        //初始化司机原料
//        DriverAuthMaterial driverAuthMaterial = new DriverAuthMaterial()
//                .setDriverId(snowflake)
//                .setId(snowflake)
//                .setName(NameUtil.getName())
//                .setCreateTime(new Date())
//                .setRealAuthStatus(RConst.Num.INT_0);
//        driverAuthMaterialService.save(driverAuthMaterial);
        return snowflake;
    }
}
